package org.hfy.collections;

import org.hfy.Constants;
import org.hfy.collections.helper.*;

import java.util.*;

/**
 * @Description: SLR 自动机
 * @Author: Takagi
 * @date: 2023/4/23
 */
public class SLRAutomaton extends LRAutomaton {
    /*
     * @Author Takagi
     * @Description: 求项目集合的闭包
     * @date: 2023/4/25
     * @param itemSet
     * @return * @return Set<SymbolString>
     **/
    @Override
    public Set<Production> getItemSetClosure(ItemSet itemSet) {
        Set<Production> mainItemSet = itemSet.coreItemSet;
        Set<Production> result = new HashSet<>();
        Set<Symbol> visited = new HashSet<>();

        for (Production production : mainItemSet) {
            // S' -> · S
            // 圆点右侧的符号 S
            Symbol itemNextSymbol = production.right.getItemNextSymbol();
            // need : 待求闭包队列
            List<Symbol> need = new LinkedList<>();
            need.add(itemNextSymbol);
            visited.add(itemNextSymbol);
            while (need.size() > 0) {
                // 从 S 出发，求非核心项目
                Symbol start = need.get(0);
                need.remove(0);

                NonTerminator nonTerminator = this.getNonTerminatorByName(start);
                if (nonTerminator != null) {
                    for (SymbolString equal : nonTerminator.equalList) {
                        SymbolString symbolStringWithItem = new SymbolString(Constants.LR0_ITEM);
                        symbolStringWithItem.extendSymbols(equal);
                        // B -> · C
                        result.add(new Production(nonTerminator.name, symbolStringWithItem));

                        Symbol firstSymbol = equal.getFirstSymbol();
                        // 若产生式右侧的首个符号是非终结符，并且还未求过闭包，则继续求闭包
                        if (Constants.isNonTerminator(firstSymbol) && !visited.contains(firstSymbol)) {
                            visited.add(firstSymbol);
                            need.add(firstSymbol);
                        }
                    }
                }
            }
        }
        return result;
    }

    /*
     * @Author Takagi
     * @Description: 获取初始项目集
     * @date: 2023/4/25
     * @param
     * @return * @return ItemSet
     **/
    @Override
    public ItemSet getInitialItemSet() {
        ItemSet itemSet = new ItemSet();
        Production start = new Production();
        start.left = this.extendGrammarProduction.left;
        start.right = new SymbolString();
        start.right.addSymbol(new LR0Item());
        start.right.extendSymbols(this.extendGrammarProduction.right);
        itemSet.addCoreItem(start);
        itemSet.nonCoreItemSet = getItemSetClosure(itemSet);
        return itemSet;
    }

    /*
     * @Author Takagi
     * @Description: 处理状态转移
     * @date: 2023/5/20
     * @param name
     * @param itemSet
     * @param itemSetFamily
     * @return * @return State
     **/
    @Override
    public State solveState(int name, ItemSet itemSet, List<ItemSet> itemSetFamily) {
        Set<Symbol> inputs = itemSet.getInputSymbols();
        State state = new State();
        state.setName(name);
        int offset = 1;
        // 对于项目集所有的输入符号
        for (Symbol input : inputs) {
            // 输入符号后，转移到的状态
            ItemSet next = move(itemSet, input);
            if (next != null) {
                // 终结符对应 shift
                if (Constants.isTerminator(input)) {
                    state.setShiftAction(input, indexOfItemSet(itemSetFamily, next));
                } else {
                    // 非终结符对应 goto
                    state.setGoto(input, indexOfItemSet(itemSetFamily, next));
                }
            }
        }
        // 对于所有的归约项目，产生式左侧符号 Follow 集合的所有符号添加 ri
        // i 为归约项目对应的产生式编号
        Set<Production> reducedItems = itemSet.getAllReducedItem();
        for (Production reducedItem : reducedItems) {
            for (Symbol symbol : getNonTerminatorByName(reducedItem.left).Follow) {
                // 如果产生式左侧的符号 与 拓广文法左侧的符号相同，则是接受状态
                if (reducedItem.left.equals(extendGrammarProduction.left)) {
                    state.setAcceptAction(symbol);
                } else {
                    state.setReduceAction(symbol, indexOfProduction(reducedItem.getProductionIgnoreItem()) + offset, reducedItem);
                }
            }
        }
        return state;
    }
}
